package com.uow.algdemo.animation;

import java.util.List;

import com.uow.algdemo.material.Point;

import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.view.animation.Animation;
import android.view.animation.TranslateAnimation;
import android.view.animation.Animation.AnimationListener;
import android.widget.RelativeLayout;
/**
 * This class will receive a set of numbers for sorting and sorting algorithm animation steps, then generate animation on animation layout.
 */
public class AnimationSort extends RelativeLayout implements AnimationInterface
{
	/**
     * This is handler for whole progress of animation.
     */
	private Handler handler;
	/**
     * This is handler for animation of compare two values.
     */
	private Handler handler_c;
	/**
     * This is current status of disks moving or not.
     */
	private boolean running = false;
	/**
     * This is current status of animation completed or not.
     */
	private boolean finished = false;
	/**
     * This is current status of pause or not.
     */
	private boolean pause = false;
	/**
     * This array contains view of integers for sorting.
     */
	private Point[] numbers;
	/**
     * This array contains positions of integers' view.
     */
	private int[] numbersPos;
	/**
     * This is the array of integers waiting for sort.
     */
	private int[] array;
    /**
     * This array contains steps of sorting animation.
     */
	private List<int[][]> steps;
    /**
     * This is the current step of animation position.
     */
	private int position = 0;
    /**
     * This is the current speed of animation.
     */
	private int speed = 10;
    /**
     * This is the size of a point or number.
     */
	private int size = 0;
    /**
     * This is the step length of move a point or number.
     */
	private int stepm = 0;
	/**
     * @param context The Context the view is running in, through which it can access the current theme, resources, etc.
	 * @param newArray The unsorted array from input.
     * @param newSteps Steps of the queens problem solution animation.
     * @param width Width of layout in pixel.
     * @param height Height of layout in pixel.
     * @param psize Size of font in pixel.
	 */
	public AnimationSort(Context context, int[] newArray, List<int[][]> newSteps, int width, int height, int psize)
	{
		super(context);
		array = newArray;
		steps = newSteps;

		size = psize;
		stepm = size + 10;

		numbers = new Point[array.length];
		numbersPos = new int[array.length];
		for(int i = 0; i < array.length; i++)
		{
			numbersPos[i] = i;
		}

		for(int i = 0; i < array.length; i++)
		{
			numbers[i] = new Point(context, array[i], size);
    		RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(size, size);
    		params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
    		params.bottomMargin = height / 2 - size / 2;
    		params.leftMargin = ((width - (array.length * (size + 10))) / 2) + (i * (size + 10));
    		this.addView(numbers[i],params);
		}
	}
	/**
	 * @param steps The steps to set
	 */
	public void setSteps(List<int[][]> newSteps)
	{
		steps = newSteps;
	}
	/**
	 * @return The speed
	 */
	public int getSpeed()
	{
		return speed;
	}
    /**
     * This method set speed of animation.
     * @param newSpeed Speed value to set.
     */	
	public void setSpeed(int newSpeed)
	{
		speed = newSpeed;
	}
	/**
	 * @return The animation status
	 */
	public boolean getStatus() {
		return finished;
	}
    /**
     * This method send feedback from this class for different command.
     * @param mes Message to current class.
     * @return An array for feedback information.
     */
	public int[][] sendMessage(int mes)
	{
		int[][] tmp = new int[1][mes];
		return tmp;
	}
   	/**
     * This method generate animation and start animation.
     */	
	@SuppressLint({ "HandlerLeak", "HandlerLeak" })
	public void runAnimation()
	{
		handler = new Handler()
		{
			public void handleMessage(Message msg)
			{
				switch (msg.what)
				{
					case 1:
					{
						if(pause == false && finished == false)
						{
							if(position < steps.size())
							{
								int[][] a = steps.get(position);
						    	switch(a[0][0])
						    	{
						    		case 0:
						    		{
						    			compare(a[1][0],a[2][0]);
						    			break;
						    		}
						    		case 1:
						    		{
							    		swap(a[1][0],a[2][0]);
							    		break;
						    		}
						    		case 2:
						    		{
						    			moveup(a[1][0]);
						    			break;
						    		}
						    		case 3:
						    		{
						    			movenext(a[1][0]);
						    			break;
						    		}
						    		case 4:
						    		{
						    			movedown(a[1][0],a[2][0]);
						    			break;
						    		}
						    	}
						    	position++;
							}
							else
							{
								finished = true;
							}
						}
						break;
					}
				}
				super.handleMessage(msg);
			}
		};

		handler.sendEmptyMessage(1);
	}
    /**
     * This method pause animation.
     */	
	public void pauseAnimation()
	{
		pause = true;
	}
    /**
     * This method resume animation from pause position.
     */	
	public void resumeAnimation()
	{
		pause = false;
		if(!running)
		{
			handler.sendEmptyMessage(1);
		}
	}
    /**
     * This method stop animation and clear layout.
     */	
	public void stopAnimation()
	{
		finished = true;
	}
    /**
     * This method show next step of animation when animation is pause.
     */	
	public void nextStep()
	{
		if(pause == true && running == false)
		{
			if(position < steps.size())
			{
				int[][] a = steps.get(position);
		    	switch(a[0][0])
		    	{
		    		case 0:
		    		{
		    			compare(a[1][0],a[2][0]);
		    			break;
		    		}
		    		case 1:
		    		{
			    		swap(a[1][0],a[2][0]);
			    		break;
		    		}
		    		case 2:
		    		{
		    			moveup(a[1][0]);
		    			break;
		    		}
		    		case 3:
		    		{
		    			movenext(a[1][0]);
		    			break;
		    		}
		    		case 4:
		    		{
		    			movedown(a[1][0],a[2][0]);
		    			break;
		    		}
		    	}
		    	position++;
			}
		}
	}
    /**
     * This method show last step of animation when animation is pause.
     */	
	public void lastStep()
	{
		if(pause == true && running == false)
		{
			if(position > 0)
			{
				position--;
				int[][] a = steps.get(position);
		    	switch(a[0][0])
		    	{
		    		case 0:
		    		{
		    			compare(a[1][0],a[2][0]);
		    			break;
		    		}
		    		case 1:
		    		{
			    		swap(a[1][0],a[2][0]);
			    		break;
		    		}
		    		case 2:
		    		{
		    			moveup(a[1][0]);
		    			break;
		    		}
		    		case 3:
		    		{
		    			movenext(a[1][0]);
		    			break;
		    		}
		    		case 4:
		    		{
		    			movedown(a[1][0],a[2][0]);
		    			break;
		    		}
		    	}
			}
		}
	}
	/**
     * This method generate animation for compare two integers.
     * @param first First integer for comparing.
     * @param second Second integer for comparing.
     */
	@SuppressLint({ "HandlerLeak", "HandlerLeak" })
	private void compare(final int first, final int second)
	{
		running = true;
		numbers[numbersPos[first]].colorGreen();
		numbers[numbersPos[second]].colorGreen();
		handler_c = new Handler()
		{
			public void handleMessage(Message msg)
			{
				switch (msg.what)
				{
					case 1:
					{
						numbers[numbersPos[first]].colorBlue();
						numbers[numbersPos[second]].colorBlue();
						running = false;
				    	handler.sendEmptyMessageDelayed(1, speed * 50);
						break;
					}
				}
			}
		};

		handler_c.sendEmptyMessageDelayed(1, speed * 400);
	}
    /**
     * This method generate animation for swap two integers.
     * @param first First integer for comparing.
     * @param second Second integer for comparing.
     */
	private void swap(int org, int tar)
	{
		final int step1 = stepm;
		final int step2;
		final int step3 = stepm;
		final int stepSpeed = speed * 150;
		
		final int first = numbersPos[org];
		final int second = numbersPos[tar];

		running = true;
		step2 = (tar - org) * stepm;

		TranslateAnimation animationStepOneA = new TranslateAnimation(0, 0, 0, step1);
		animationStepOneA.setDuration(stepSpeed);
        numbers[first].startAnimation(animationStepOneA);

        TranslateAnimation animationStepOneB = new TranslateAnimation(0, 0, 0, 0 - step1);
        animationStepOneB.setDuration(stepSpeed);
        numbers[second].startAnimation(animationStepOneB);

        animationStepOneA.setAnimationListener(new AnimationListener()
        {
	        public void onAnimationEnd(Animation animation)
	        {
	        	numbers[first].clearAnimation();
	        	numbers[first].layout(numbers[first].getLeft(), numbers[first].getTop() + step1, numbers[first].getRight(), numbers[first].getBottom() + step1);

	        	numbers[second].clearAnimation();
	        	numbers[second].layout(numbers[second].getLeft(), numbers[second].getTop() - step1, numbers[second].getRight(), numbers[second].getBottom() - step1);

	        	TranslateAnimation animationStepTwoA = new TranslateAnimation(0, step2, 0, 0);
	        	animationStepTwoA.setDuration(stepSpeed);
	            numbers[first].startAnimation(animationStepTwoA);

	            TranslateAnimation animationStepTwoB = new TranslateAnimation(0, 0 - step2, 0, 0);
	            animationStepTwoB.setDuration(stepSpeed);
	            numbers[second].startAnimation(animationStepTwoB);

	            animationStepTwoA.setAnimationListener(new AnimationListener()
	            {
	    	        public void onAnimationEnd(Animation animation)
	    	        {
	    	        	numbers[first].clearAnimation();
	    	        	numbers[first].layout(numbers[first].getLeft() + step2, numbers[first].getTop(), numbers[first].getRight() + step2, numbers[first].getBottom());
	    	        	numbers[second].clearAnimation();
	    	        	numbers[second].layout(numbers[second].getLeft() - step2, numbers[second].getTop(), numbers[second].getRight() - step2, numbers[second].getBottom());

	    	        	TranslateAnimation animationStepThreeA = new TranslateAnimation(0, 0, 0, 0 - step3);
	    	        	animationStepThreeA.setDuration(stepSpeed);
	    	            numbers[first].startAnimation(animationStepThreeA);

	    	            TranslateAnimation animationStepThreeB = new TranslateAnimation(0, 0, 0, step3);
	    	            animationStepThreeB.setDuration(stepSpeed);
	    	            numbers[second].startAnimation(animationStepThreeB);

	    	            animationStepThreeA.setAnimationListener(new AnimationListener()
	    	            {
	    	    	        public void onAnimationEnd(Animation animation)
	    	    	        {
	    	    	        	numbers[first].clearAnimation();
	    	    	        	numbers[first].layout(numbers[first].getLeft(), numbers[first].getTop() - step3, numbers[first].getRight(), numbers[first].getBottom() - step3);

	    	    	        	numbers[second].clearAnimation();
	    	    	        	numbers[second].layout(numbers[second].getLeft(), numbers[second].getTop() + step3, numbers[second].getRight(), numbers[second].getBottom() + step3);
	    	    	    		running = false;
	    	    	        	handler.sendEmptyMessage(1);
	    	    	        }

	    	    			public void onAnimationRepeat(android.view.animation.Animation arg0) {}
	    	    			public void onAnimationStart(android.view.animation.Animation arg0) {}
	    	            });
	    	        }

	    			public void onAnimationRepeat(android.view.animation.Animation arg0) {}
	    			public void onAnimationStart(android.view.animation.Animation arg0) {}
	            });
	        }

			public void onAnimationRepeat(android.view.animation.Animation arg0) {}
			public void onAnimationStart(android.view.animation.Animation arg0) {}
        });

        int tmp = numbersPos[org];
        numbersPos[org] = numbersPos[tar];
        numbersPos[tar] = tmp;
	}
    /**
     * This method generate animation for move up an integer.
     * @param index Index of integer for move up.
     */
	private void moveup(final int index)
	{
		final int step = stepm;
		final int stepSpeed = speed * 150;

		running = true;

		TranslateAnimation animation = new TranslateAnimation(0, 0, 0, -step);
		animation.setDuration(stepSpeed);
        numbers[numbersPos[index]].startAnimation(animation);
        animation.setAnimationListener(new AnimationListener()
        {
	        public void onAnimationEnd(Animation animation)
	        {
	        	numbers[numbersPos[index]].clearAnimation();
	        	numbers[numbersPos[index]].layout(numbers[numbersPos[index]].getLeft(), numbers[numbersPos[index]].getTop() - step, numbers[numbersPos[index]].getRight(), numbers[numbersPos[index]].getBottom() - step);
	    		running = false;
	        	handler.sendEmptyMessage(1);
	        }

			public void onAnimationRepeat(android.view.animation.Animation arg0) {}
			public void onAnimationStart(android.view.animation.Animation arg0) {}
        });
	}
    /**
     * This method generate animation for move an integer to next position.
     * @param index Index of integer for moving.
     */
	private void movenext(final int index)
	{
		final int step = stepm;
		final int stepSpeed = speed * 150;

		running = true;

		TranslateAnimation animation=new TranslateAnimation(0, step, 0, 0);
		animation.setDuration(stepSpeed);
        numbers[numbersPos[index]].startAnimation(animation);
        animation.setAnimationListener(new AnimationListener()
        {
	        public void onAnimationEnd(Animation animation)
	        {
	        	numbers[numbersPos[index]].clearAnimation();
	        	numbers[numbersPos[index]].layout(numbers[numbersPos[index]].getLeft() + step, numbers[numbersPos[index]].getTop(), numbers[numbersPos[index]].getRight() + step, numbers[numbersPos[index]].getBottom());
	    		running = false;
	        	handler.sendEmptyMessage(1);
	        }

			public void onAnimationRepeat(android.view.animation.Animation arg0) {}
			public void onAnimationStart(android.view.animation.Animation arg0) {}
        });
    }

    /**
     * This method generate animation for move down an integer to empty position.
     * @param indexSource Index of integer before moving.
     * @param indexTarget Index of integer after moving.
     */
	private void movedown(final int indexSource, final int indexTarget)
	{
		final int stepH;
		final int stepV = stepm;
		final int stepSpeed = speed * 150;
		
		running = true;
		stepH = (indexTarget - indexSource) * stepm;

		TranslateAnimation animationStepOne = new TranslateAnimation(0, stepH, 0, 0);
		animationStepOne.setDuration(stepSpeed);
        numbers[numbersPos[indexSource]].startAnimation(animationStepOne);
        animationStepOne.setAnimationListener(new AnimationListener(){
	        public void onAnimationEnd(Animation animation) {
	        	numbers[numbersPos[indexSource]].clearAnimation();
	        	numbers[numbersPos[indexSource]].layout(numbers[numbersPos[indexSource]].getLeft() + stepH, numbers[numbersPos[indexSource]].getTop(), numbers[numbersPos[indexSource]].getRight() + stepH, numbers[numbersPos[indexSource]].getBottom());

	        	TranslateAnimation animationStepTwo = new TranslateAnimation(0, 0, 0, stepV);
	        	animationStepTwo.setDuration(stepSpeed);
	            numbers[numbersPos[indexSource]].startAnimation(animationStepTwo);

	            animationStepTwo.setAnimationListener(new AnimationListener()
	            {
	    	        public void onAnimationEnd(Animation animation)
	    	        {
	    	        	numbers[numbersPos[indexSource]].clearAnimation();
	    	        	numbers[numbersPos[indexSource]].layout(numbers[numbersPos[indexSource]].getLeft(), numbers[numbersPos[indexSource]].getTop() + stepV, numbers[numbersPos[indexSource]].getRight(), numbers[numbersPos[indexSource]].getBottom() + stepV);
	    	            int tmp = numbersPos[indexSource];
	    	            for(int i = indexSource; i > indexTarget; i--)
	    	            {
	    	            	numbersPos[i] = numbersPos[i - 1];
	    	            }
	    	            numbersPos[indexTarget] = tmp;
	    	    		running = false;
	    	        	handler.sendEmptyMessage(1);
	    	        }

	    			public void onAnimationRepeat(android.view.animation.Animation arg0) {}
	    			public void onAnimationStart(android.view.animation.Animation arg0) {}
	            });
	        }

			public void onAnimationRepeat(android.view.animation.Animation arg0) {}
			public void onAnimationStart(android.view.animation.Animation arg0) {}
        });
	}
}
